Docly JavaScript is not Node
-
What you'll see
Code written for a Docly workspace opens with
const fs = require("fs"), callsawait fetch(url), readsprocess.env.API_KEY, or arrives with apackage.jsonand annpm installstep. An AI coding agent asked to add a dependency proposes installing a package.None of it works, and the failure is rarely a clean error — it is an undefined symbol part-way through a page render, so the page just stops producing output at that point.
-
What's actually happening
Docly's server-side JavaScript engine is HashJS. It is Docly's own engine, written in .NET: the language is JavaScript, but the runtime around it is Docly's, not Node's. Almost nothing from the Node standard library is present, and there is no package manager: the absence of
package.jsonandnode_modulesin a Docly workspace is the normal state, not a missing setup step.The same applies in the browser direction — this is not browser JavaScript either. There is no
window, nodocument, and nofetchon the server side.One name genuinely collides.
docly.process()exists as a script evaluator — it executes a Docly script string with a supplied set of globals, and requires specific access because of the back-door risk. It has nothing to do with Node'sprocessobject. There is also a bare globalprocess, which is that same evaluator rather than Node's: it is a function, andprocess.envisnull, soprocess.env.API_KEYfails on a null property access instead of quietly readingundefined.That points at the wider trap. The absent names are declared and null, not undefined, which breaks the standard feature-detection idiom:
typeof fetch // "object" -- because fetch is null typeof fetch !== "undefined" // true -- so the detect passes fetch(url) // and then fails anywayMeasured inventory, re-checked after the August 2026 engine update:
Declared but nullPresent and usable require,fetch,window,document,module,exports,setTimeoutconsole,JSON,Math,Date,Promise,Buffer,crypto,btoa,atob,processbtoaandatobwork as expected —btoa("abc")returns"YWJj".Bufferandcryptoresolve to objects rather than being absent, but presence is not a promise of Node semantics: check what a member actually does before relying on it. AndsetTimeoutbeingnullmeans there are no timers at all — there is nothing to defer work onto.What you get instead is a single platform library,
docly.*, covering filesystem, HTTP, mail, images, authentication, sharing and output; plusimportfor your own code. The standard built-ins are there as usual:JSON,Math,String,Array,Date,console.log. -
What to do
Reach for the Docly equivalent rather than the Node idiom:
Instead of Use require("fs"),fs.readFileFilesystem functions — docly.getFile,docly.getFiles,docly.getJson,docly.saveFile,docly.saveJsonfetch,axios,node-fetchNetwork functions — docly.httpGet(JSON-parsed body),docly.httpPost,docly.httpFetch(full response: status, headers, raw body)nodemailerMail functions — smtp.send, plus POP3 and IMAPprocess.env.SECRETA JSON file under #/, read withdocly.getJson(). The#/tree is never served over HTTP — see Keep private data under the # foldernpm install <pkg>A Docly package, or your own .jsfile under#/require("./helpers")import { helper } from "#/helpers.js"— absolute paths from site root, no relative./. Named imports, namespace imports (import * as m from "…") andimport("…")work; a default import (import x from "…") does notmodule.exportsexport default/ namedexportExpress req/resAuto-bound function parameters plus the queryandformglobals; the returned object is the response. See Reading query and form fields in API callspath.join, relative pathsAbsolute paths from site root. In anything the browser will execute, use ~/— see Use tilde paths in JavaScriptMost of these exist only in
#/API/scripts. The network functions (httpGet,httpPost,httpFetch…), the mail functions, the share functions and every function that writes are registered for API scripts only; in a.hashpage the call fails with is not a function. Reading functions such asgetFile,getFilesandgetJsonwork everywhere. See Some docly functions exist only in API scripts.A minimal API endpoint, for orientation:
// #/API/orders/list.js import { format } from "#/helpers.js"; export default () => { let rows = docly.getFiles("/Orders", { recursive: true }); return { count: rows.length, items: rows.map(format) }; };Do not feature-detect with
typeof. Because the absent names arenullrather than undefined,typeof x !== "undefined"is true for every one of them. Testx === nullif you must, or better, do not reach for the Node name at all.When you are unsure what a function returns, print it rather than assuming a Node-shaped result. A throwaway
.hashpage is the fastest way — see Use a scratch hash file to test Docly functions.Start from the JavaScript reference, and read About JavaScript and HASH files before writing much of anything.
Last reviewed 17 Oct 2026